﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text.RegularExpressions;

using umbraco.BusinessLogic;
using umbraco.cms.businesslogic.web;
using umbraco.cms.businesslogic.media;

namespace MassContentUploader.DataTypes.MassUploader {
    public partial class MassUploaderControl : System.Web.UI.UserControl, umbraco.editorControls.userControlGrapper.IUsercontrolDataEditor {
        int userId;
        string extractBase = System.Web.HttpContext.Current.Server.MapPath("~/App_data");
        char[] invalidFileNameChars = new char[]{ '_', '-', '!', '.'};
        bool isMedia;

        protected global::System.Web.UI.WebControls.PlaceHolder phContent;
        protected global::System.Web.UI.WebControls.Label lblLabel;
        protected global::System.Web.UI.WebControls.Label lblError;
        protected global::System.Web.UI.WebControls.FileUpload uplArchive;
        protected global::System.Web.UI.WebControls.CheckBox chkRecursive;
        protected global::System.Web.UI.WebControls.TextBox txtPassword;
        protected global::System.Web.UI.WebControls.DropDownList ddlNodeTypeFolder;
        protected global::System.Web.UI.WebControls.DropDownList ddlNodeTypeFile;

        public string umbracoValue;

        protected override void OnInit(EventArgs e) {
            base.OnInit(e);
        }

        protected void Page_Load(object sender, EventArgs e) {
            isMedia = Path.GetFileNameWithoutExtension(Request.Url.AbsolutePath) == "editMedia";
            userId = umbraco.BasePages.UmbracoEnsuredPage.GetUserId(umbraco.BasePages.UmbracoEnsuredPage.umbracoUserContextID);
            phContent.Visible = false;

            int id = Convert.ToInt32(Request["id"]);
            if (id > 0) {

                if (!Page.IsPostBack) {
                    int[] allowedChildContentType;

                    if (!isMedia) {
                        allowedChildContentType = new Document(id).ContentType.AllowedChildContentTypeIDs;
                    } else {
                        allowedChildContentType = new Media(id).ContentType.AllowedChildContentTypeIDs;
                    }

                    foreach (int dtId in allowedChildContentType) {
                        string typeValue = this.isMedia ? new MediaType(dtId).Alias : new DocumentType(dtId).Alias;
                        string typeText = this.isMedia ? new MediaType(dtId).Text : new DocumentType(dtId).Text;

                        ddlNodeTypeFolder.Items.Add(new ListItem(typeText, typeValue));
                        ddlNodeTypeFile.Items.Add(new ListItem(typeText, typeValue));

                        if (typeValue.ToLower().Contains("folder")) {
                            ddlNodeTypeFolder.SelectedIndex = ddlNodeTypeFolder.Items.Count - 1;
                        } else {
                            if (typeValue.ToLower().Contains("file") || typeValue.ToLower().Contains("document") || typeValue.ToLower().Contains("picture") || typeValue.ToLower().Contains("image")) {
                                ddlNodeTypeFile.SelectedIndex = ddlNodeTypeFile.Items.Count - 1;
                            }
                        }
                    }

                }


                phContent.Visible = true;
                if (Page.IsPostBack && uplArchive.HasFile) {
                    if (Path.GetExtension(uplArchive.FileName).ToLower() == ".zip") {
                        string filename = string.Format("ZipHandler_{0}_{1}{2}",
                                                        Path.GetFileNameWithoutExtension(uplArchive.FileName),
                                                        this.GenerateSeed(),
                                                        Path.GetExtension(uplArchive.FileName));
                        string archivePath = extractBase + "/" + filename;
                        string destination = extractBase + "/" + filename + "_tmp";
                        uplArchive.SaveAs(archivePath);
                        Directory.CreateDirectory(destination);

                        ZipHandler extract = new ZipHandler(archivePath, destination, chkRecursive.Checked, txtPassword.Text);
                        switch (extract.Result) {
                            case ZipHandler.ReturnCodes.Success:
                                DirectoryInfo root = new DirectoryInfo(destination);
                                this.CreateFolderStructure(id, root);

                                lblError.Text = "Archive extracted successfully.";
                                break;
                            case ZipHandler.ReturnCodes.EmptyArchive:
                                lblError.Text = "Cannot process archive: 0 files found.";
                                break;
                            default:
                                lblError.Text = "Archive is corrupted or unreadable.";
                                break;
                        }

                        File.Delete(archivePath);
                        Directory.Delete(destination, true);
                    } else {
                        lblError.Text = uplArchive.FileName + " is not a valid ZIP file.";
                    }

                }
            }

        }


        public object value {
            get {
                return umbracoValue;
            }
            set {
                umbracoValue = value.ToString();
            }
        }

        void CreateFolderStructure(int parentNodeId, DirectoryInfo folder) {
            DirectoryInfo[] folders = folder.GetDirectories();
            FileInfo[] files = folder.GetFiles();
            foreach (DirectoryInfo current in folders) {
                string nodeName = this.CleanFileName(current.Name);
                // root section:
                // media
                if (this.isMedia) {
                    Media m = this.createMedia(parentNodeId, nodeName, ddlNodeTypeFolder.SelectedItem.Value, userId);
                    m.Save();
                    this.CreateFolderStructure(m.Id, current);
                    // content
                } else {
                    Document doc = this.createDocument(parentNodeId, nodeName, ddlNodeTypeFolder.SelectedItem.Value, userId);
                    doc.Save();
                    this.CreateFolderStructure(doc.Id, current);
                }             
            }

            foreach (FileInfo current in files) {
                string nodeName = this.CleanFileName(System.IO.Path.GetFileNameWithoutExtension(current.Name));

                string umbracoDirUri  = "/media/" + parentNodeId;
                string umbracoDirPath = HttpContext.Current.Server.MapPath(umbracoDirUri);

                // ensure unique filename
                string umbracoFileName = string.Format("{0}_{1}{2}",
                                                    Path.GetFileNameWithoutExtension(current.Name),
                                                    this.GenerateSeed(),
                                                    Path.GetExtension(current.Name));
                string umbracoExtension = Path.GetExtension(current.Name).Substring(1);
                string umbracoFileUri = umbracoDirUri + "/" + umbracoFileName;
                string umbracoFilePath = HttpContext.Current.Server.MapPath(umbracoFileUri);
                long umbracoBytes = current.Length;

                if (!Directory.Exists(umbracoDirPath)) {
                    Directory.CreateDirectory(umbracoDirPath);
                }


                File.Move(current.FullName, umbracoFilePath);

                // media
                if (this.isMedia) {
                    Media m = this.createMedia(parentNodeId, nodeName, ddlNodeTypeFile.SelectedItem.Value, userId);
                    this.SetMediaProperty(m, "umbracoFile", umbracoFileUri);
                    this.SetMediaProperty(m, "umbracoBytes", umbracoBytes);
                    this.SetMediaProperty(m, "umbracoExtension", umbracoExtension);
                    m.Save();
                    // content
                } else {
                    Document doc = this.createDocument(parentNodeId, nodeName, ddlNodeTypeFile.SelectedItem.Value, userId);
                    this.SetDocumentProperty(doc, "umbracoFile", umbracoFileUri);
                    this.SetDocumentProperty(doc, "umbracoBytes", umbracoBytes);
                    this.SetDocumentProperty(doc, "umbracoExtension", umbracoExtension);
                    doc.Save();
                }
            }        
        }

        // FIXME: find a more efficient way to check wether dealing with content or media
        Media createMedia(int parentId, string nodeName, string nodeTypeAlias, int userId) {
            Media parent;
            try {
                parent = new Media(parentId);
            } catch {
                return null;
            }    

            Media m = Media.MakeNew(nodeName, MediaType.GetByAlias(nodeTypeAlias), User.GetUser(userId), parentId);
            return m;
        }

        Document createDocument(int parentId, string nodeName, string nodeTypeAlias, int userId) {
            Document parent;
            try {
                parent = new Document(parentId);
            } catch {
                return null;
            }

            Document doc = Document.MakeNew(nodeName, DocumentType.GetByAlias(nodeTypeAlias), User.GetUser(userId), parentId);

            return doc;
        }

        string CleanFileName(string input){
            for (int i = 0; i < this.invalidFileNameChars.Length; i++) {
                input = input.Replace(this.invalidFileNameChars[i], ' ');   
            }
            Regex clean = new Regex("[ ]{2,}");
            input = clean.Replace(input, " ");
            
            return input;
        }

        void SetMediaProperty(Media context, string key, object value) {
            umbraco.cms.businesslogic.property.Property prop = context.getProperty(key);

            if (prop != null) prop.Value = value;
        }

        void SetDocumentProperty(Document context, string key, object value) {
            umbraco.cms.businesslogic.property.Property prop = context.getProperty(key);

            if (prop != null) prop.Value = value;
        }

        string GenerateSeed() {
            return System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(DateTime.Now.ToString() + "_" + new Guid().ToString(), "sha1");
        }
    }
}